﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Reflection;
using System.Web;
using System.Web.Configuration;

namespace ProviderLogging
{
    public class SiteMapLog : SiteMapProvider
    {
        public const string cnstInternalProviderName = "internalProviderName";

        private SiteMapProvider internalProvider = null;
        protected SiteMapProvider InternalProvider
        {
            get 
            {
                if (internalProvider == null) { internalProvider = GetInternalProvider(); }
                return (internalProvider);
            }
        }

        private string internalProviderName = null;
        protected string InternalProviderName
        {
            get { return (internalProviderName); }
        }

        protected string _name = null;

        protected SiteMapProvider GetInternalProvider()
        {
            SiteMapProvider result = null;
            try
            {
                result = SiteMap.Providers[InternalProviderName];

                SiteMapSection siteMapSect = (SiteMapSection)System.Configuration.ConfigurationManager.GetSection("system.web/siteMap");
                ProviderSettings settings = siteMapSect.Providers[InternalProviderName];
                result.Initialize(InternalProviderName, settings.Parameters);
            }
            catch (InvalidOperationException)
            {
                // Ignore, it's telling us that it's already been initialized
            }
            catch (IndexOutOfRangeException)
            {
                Logging.WriteString("Initialize failed. Unable to locate the provider");
                throw;
            }
            catch (Exception excpt)
            {
                Logging.WriteString("GetInternalProvider Exception " + excpt.ToString());
                throw;
            }

            return result;
        }

        public SiteMapLog()
        {
            Logging.WriteString("SiteMapLog started");
        }

        public override void Initialize(string name, NameValueCollection attributes)
        {
            _name = name;
            internalProviderName = attributes[cnstInternalProviderName];
            Logging.LogOperation("Initialize", new object[] { name, attributes }, null, null);
         }

        public override SiteMapNode CurrentNode
        {
            get
            {
                SiteMapNode retVal = null;
                try
                {
                    retVal = InternalProvider.CurrentNode;
                    Logging.LogOperation("CurrentNode!Get", null, retVal, null);
                    return retVal;
                }
                catch (Exception e)
                {
                    Logging.LogOperation("CurrentNode!Get", null, retVal, e);
                    throw;
                }
            }
        }

        public override string Description
        {
            get
            {
                string retVal = null;
                try
                {
                    retVal = InternalProvider.Description;
                    Logging.LogOperation("Description!Get", null, retVal, null);
                    return retVal;
                }
                catch (Exception e)
                {
                    Logging.LogOperation("Description!Get", null, retVal, e);
                    throw;
                }
            }
        }

        public override SiteMapNode FindSiteMapNode(string rawUrl)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.FindSiteMapNode(rawUrl);
                Logging.LogOperation("FindSiteMapNode(string)", new object[] { rawUrl }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("FindSiteMapNode(string)", new object[] { rawUrl }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode FindSiteMapNode(HttpContext context)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.FindSiteMapNode(context);
                Logging.LogOperation("FindSiteMapNode(HttpContext)", new object[] { context }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("FindSiteMapNode(HttpContext)", new object[] { context }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode FindSiteMapNodeFromKey(string key)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.FindSiteMapNodeFromKey(key);
                Logging.LogOperation("FindSiteMapNodeFromKey", new object[] { key }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("FindSiteMapNodeFromKey", new object[] { key }, retVal, e);
                throw;
            }
        }

        public override SiteMapNodeCollection GetChildNodes(SiteMapNode node)
        {
            SiteMapNodeCollection retVal = null;

            try
            {
                retVal = InternalProvider.GetChildNodes(node);
                Logging.LogOperation("GetChildNodes", new object[] { node }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetChildNodes", new object[] { node }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode GetCurrentNodeAndHintAncestorNodes(int upLevel)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.GetCurrentNodeAndHintAncestorNodes(upLevel);
                Logging.LogOperation("GetCurrentNodeAndHintAncestorNodes", new object[] { upLevel }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetCurrentNodeAndHintAncestorNodes", new object[] { upLevel }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes(int upLevel, int downLevel)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.GetCurrentNodeAndHintNeighborhoodNodes(upLevel, downLevel);
                Logging.LogOperation("GetCurrentNodeAndHintNeighborhoodNodes", new object[] { upLevel, downLevel }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetCurrentNodeAndHintNeighborhoodNodes", new object[] { upLevel, downLevel }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode GetParentNode(SiteMapNode node)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.GetParentNode(node);
                Logging.LogOperation("GetParentNode", new object[] { node }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetParentNode", new object[] { node }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(int walkupLevels, int relativeDepthFromWalkup)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(walkupLevels, relativeDepthFromWalkup);
                Logging.LogOperation("GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(int, int)", new object[] { walkupLevels, relativeDepthFromWalkup }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(int, int)", new object[] { walkupLevels, relativeDepthFromWalkup }, retVal, e);
                throw;
            }
        }

        public override SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent(SiteMapNode node, int walkupLevels, int relativeDepthFromWalkup)
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.GetParentNodeRelativeToNodeAndHintDownFromParent(node, walkupLevels, relativeDepthFromWalkup);
                Logging.LogOperation("GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(SiteMapNode, int, int)", new object[] { node, walkupLevels, relativeDepthFromWalkup }, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetParentNodeRelativeToCurrentNodeAndHintDownFromParent(SiteMapNode, int, int)", new object[] { node, walkupLevels, relativeDepthFromWalkup }, retVal, e);
                throw;
            }
        }

        public override void HintAncestorNodes(SiteMapNode node, int upLevel)
        {
            try
            {
                InternalProvider.HintAncestorNodes(node, upLevel);
                Logging.LogOperation("HintAncestorNodes", new object[] { node, upLevel }, null, null);
            }
            catch (Exception e)
            {
                Logging.LogOperation("HintAncestorNodes", new object[] { node, upLevel }, null, e);
                throw;
            }
        }

        public override void HintNeighborhoodNodes(SiteMapNode node, int upLevel, int downLevel)
        {
            try
            {
                InternalProvider.HintNeighborhoodNodes(node, upLevel, downLevel);
                Logging.LogOperation("HintNeighborhoodNodes", new object[] { node, upLevel, downLevel }, null, null);
            }
            catch (Exception e)
            {
                Logging.LogOperation("HintNeighborhoodNodes", new object[] { node, upLevel, downLevel }, null, e);
                throw;
            }
        }

        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
        {
            bool? retVal = null;

            try
            {
                retVal = InternalProvider.IsAccessibleToUser(context, node);
                Logging.LogOperation("IsAccessibleToUser", new object[] { context, node }, retVal, null);
                return ((bool)(retVal));
            }
            catch (Exception e)
            {
                Logging.LogOperation("IsAccessibleToUser", new object[] { context, node }, retVal, e);
                throw;
            }
        }

        public override string Name
        {
            get
            {
                string retVal = null;
                try
                {
                    retVal = InternalProvider.Name;
                    Logging.LogOperation("Name!Get", null, retVal, null);
                    return retVal;
                }
                catch (Exception e)
                {
                    Logging.LogOperation("Name!Get", null, retVal, e);
                    throw;
                }
            }
        }

        // TODO: Should we be returning InternalProvider instead?
        public override SiteMapProvider ParentProvider
        {
            get
            {
                SiteMapProvider retVal = null;
                try
                {
                    retVal = InternalProvider.ParentProvider;
                    Logging.LogOperation("ParentProvider!Get", null, retVal, null);
                    return retVal;
                }
                catch (Exception e)
                {
                    Logging.LogOperation("ParentProvider!Get", null, retVal, e);
                    throw;
                }
            }
            set
            {
                try
                {
                    InternalProvider.ParentProvider = value;
                    Logging.LogOperation("ParentProvider!Set", new object[] { value }, null, null);
                }
                catch (Exception e)
                {
                    Logging.LogOperation("ParentProvider!Set", new object[] { value }, null, e);
                    throw;
                }
            }
        }

        public override SiteMapNode RootNode
        {
            get
            {
                SiteMapNode retVal = null;
                try
                {
                    retVal = InternalProvider.RootNode;
                    Logging.LogOperation("RootNode!Get", null, retVal, null);
                    return retVal;
                }
                catch (Exception e)
                {
                    Logging.LogOperation("RootNode!Get", null, retVal, e);
                    throw;
                }
            }
        }

        public override SiteMapProvider RootProvider
        {
            get
            {
                SiteMapProvider retVal = null;
                try
                {
                    retVal = InternalProvider.RootProvider;
                    Logging.LogOperation("RootProvider!Get", null, retVal, null);
                    return retVal;
                }
                catch (Exception e)
                {
                    Logging.LogOperation("RootProvider!Get", null, retVal, e);
                    throw;
                }
            }
        }

        protected override SiteMapNode GetRootNodeCore()
        {
            SiteMapNode retVal = null;
            try
            {
                retVal = InternalProvider.RootNode;
                Logging.LogOperation("GetRootNodeCore", null, retVal, null);
                return retVal;
            }
            catch (Exception e)
            {
                Logging.LogOperation("GetRootNodeCore", null, retVal, e);
                throw;
            }
        }
    }
}
